import { _decorator, director, JsonAsset, Prefab, ProgressBar, resources, Sprite, Color, Label, instantiate, tween, Component, Node, AudioSource, Animation, find, AudioClip, SpriteFrame, Vec3, UITransform } from 'cc';
const { ccclass, property } = _decorator;
import { Utils } from './Utils';
import { PixelItem } from './PixelItem';
import { GameManager } from './GameManager';
import { colorHexArray } from './Color';
import { Player } from './Player';

@ccclass('Boss')
export class Boss extends Component {
    @property({type: Prefab})
    equipmentItemPrefab: Prefab = null!
    
    @property({type: Prefab})
    pixelItemPrefab: Prefab = null!

    @property({type: Prefab})
    damagePrefab: Prefab = null!

    @property({type: Node})
    HPValueNode: Node = null!

    @property({type: Node})
    HPProgressBarNode: Node = null!

    @property({type: SpriteFrame})
    crySpriteFrame: SpriteFrame = null!

    @property({type: JsonAsset})
    equipmentJsonAsset: JsonAsset = null!

    @property({type: AudioClip})
    bossMoveAudio: AudioClip = null!

    @property({type: AudioClip})
    hitBossAudio:AudioClip = null!
    
    @property({type: AudioClip})
    dieBossAudio:AudioClip = null!

    @property({type: AudioClip})
    bossAttackAudio:AudioClip = null!

    @property({type: AudioClip})
    winAudio:AudioClip = null!

    isDead: boolean = false
    isAngry: boolean = false
    isAttacking: boolean = false

    speed: number = 1
    defense: number = 50
    damage: number = 200
    life: number = 10000
    pixelNumDropped: number = 30
    equipmentJsonData: object = null

    utils: Utils = null
    audioSource: AudioSource = null
    gameManager: GameManager = null
    bossAnimationComp: Animation = null

    start() {
    
    }
    
    onEnable() {
        this.initComponents()
        this.resetProperties()
        this.setRandomStartPos()
        this.attackRandomly()

        this.bossAnimationComp.play('bossDefaultMove')
        this.equipmentJsonData = this.equipmentJsonAsset.json
    }

    /** 
     * @zh
     * 重置Boss属性。
    */
    resetProperties() {
        this.isAngry = false
        this.isDead = false
        this.speed = 1
        this.defense = 50
        this.damage = 200 
        this.life = 10000
        this.pixelNumDropped = 30

        this.HPValueNode.getComponent(Label).string = `${this.life}/${this.life}`
        this.HPProgressBarNode.getComponent(ProgressBar).progress = 1
        this.node.getComponent(Sprite).color = new Color().fromHEX('ffffff')
    }

    /** 
     * @zh
     * 初始化组件。
    */
    initComponents() {
        this.utils = find('Canvas').getComponent(Utils)
        this.audioSource = find('Canvas').getComponent(AudioSource)
        this.gameManager = find('Canvas').getComponent(GameManager)
        this.bossAnimationComp = this.node.getComponent(Animation)
    }

    /** 
     * @zh
     * 设置随机开始位置。
    */
    setRandomStartPos() {
        let canvas = find('Canvas')
        let canvasWidth = canvas.getComponent(UITransform).width
        let canvasHeight = canvas.getComponent(UITransform).height
        let bossWidth = this.node.getComponent(UITransform).width
        let bossHeight = this.node.getComponent(UITransform).height

        let x = 0
        let y = 0
        let randomIndex = Math.floor(Math.random() * 4)

        // 在画布上面
        if (randomIndex == 0) {
            x = Math.random() * canvasWidth - canvasWidth / 2
            y = canvasHeight / 2 + bossHeight
        }
        // 在画布下面
        else if (randomIndex == 1) {
            x = Math.random() * canvasWidth - canvasWidth / 2
            y = -canvasHeight / 2 - bossHeight
        }
        // 在画布左边
        else if (randomIndex == 2) {
            x = -canvasWidth / 2 - bossWidth
            y = Math.random() * canvasHeight - canvasHeight / 2
        }
        else {
            x = canvasWidth / 2 + bossWidth
            y = Math.random() * canvasHeight - canvasHeight / 2
        }

        this.node.setPosition(x, y, 0)
    }

    /** 
     * @zh
     * 播放Boss移动音效(动画帧事件)。
    */
    playBossMoveAudio() {
        this.audioSource.playOneShot(this.bossMoveAudio, 0.2)
    }

    /** 
     * @zh
     * Boss移动。
    */
    move(speed: number) {
        // 不断朝主角移动
        let targetNodePos = find('Canvas/Player').position
        let distance = new Vec3(this.node.position).subtract(targetNodePos).length()

        // 距离太近则停止移动动画
        if (distance < this.node.getComponent(UITransform).width/1.8) {
            this.bossAnimationComp.pause()
            return
        }
        else {
            this.bossAnimationComp.resume()
        }

        // 更新Boss位置
        let dir = new Vec3(this.node.position).subtract(targetNodePos).normalize()
        let pos = new Vec3(this.node.position).subtract3f(speed*dir.x, speed*dir.y, 0)
        this.node.setPosition(pos)

        // 设置方向
        if (targetNodePos.x >= this.node.position.x) {
            this.node.setScale(1, 1, 1)
        }
        else {
            this.node.setScale(-1, 1, 1)
        }
    }

    /** 
     * @zh
     * 减少生命值。
    */
    reduceLife(damageValueFromPlayer: number) {
        //出现Miss
        if (damageValueFromPlayer - this.defense <= 0) {
            this.showDamageValue(0)
        }

        // 受到伤害
        else {
            let realDamage = damageValueFromPlayer - this.defense
            this.life -= realDamage

            // 死亡
            if (this.life <= 0) {
                this.life = 0
                this.showDamageValue(realDamage)
                this.die()
            }

            // 扣除生命值
            else {
                // 播放击中音频
                this.stepBack()
                this.showDamageValue(realDamage)
                if (this.life < 5000) {
                    this.getAngry()
                }

                // 40%概率出现颜料
                if (Math.random() <= 0.4) {
                    this.spawnPixelItem()
                }

                // 20%概率出现制作图
                if (Math.random() < 0.2) {   
                    let targetNodePos = find('Canvas/Player').position
                    let dir = new Vec3(this.node.position).subtract(targetNodePos).normalize()
                    this.spawnEquipmentItem(dir)
                }
            }

            // 更新进度条
            this.HPProgressBarNode.getComponent(ProgressBar).progress = parseFloat((this.life / 10000).toFixed(3))
            this.HPValueNode.getComponent(Label).string = `10000/${this.life}`
        }
    }

    /** 
     * @zh
     * Boss死亡。
    */
    die() {
        this.isDead = true
        this.audioSource.playOneShot(this.dieBossAudio)
        this.bossAnimationComp.stop()
        this.node.getComponent(Sprite).spriteFrame = this.crySpriteFrame
        this.node.getComponent(Sprite).color = new Color().fromHEX('ffffff')

        this.unscheduleAllCallbacks()

        // 要显示第几位勇士
        let playerNum = 1
        if (!localStorage.getItem('lp-player-num')) {
            playerNum = 1
        }
        else {
            playerNum = parseInt(localStorage.getItem('lp-player-num')) + 1
        }

        // 播放胜利音效
        this.audioSource.playOneShot(this.winAudio)

        // 显示战胜文字，设置缓存
        this.utils.showFlashMessage(`恭喜第${playerNum}勇士!\n你战胜了大魔王!!!`, -1)
        localStorage.setItem('lp-win', playerNum.toString())

        // 重新加载场景
        this.scheduleOnce(()=>{
            director.loadScene('game')
        }, 6)
    }

    /** 
     * @zh
     * 被攻击时后退。
    */
    stepBack() {
        this.audioSource.playOneShot(this.hitBossAudio, 1)
        let targetNodePos = find('Canvas/Player').position
        let dir = new Vec3(this.node.position).subtract(targetNodePos).normalize()
        tween(this.node).by(0.1, {'position': new Vec3(100*dir.x, 100*dir.y, 0)}, {'easing': 'sineInOut'}).start()
    }


    /** 
     * @zh
     * 显示受到的伤害。
    */
    showDamageValue(damageValue: number) {
        let damageNode = instantiate(this.damagePrefab)
        damageNode.getComponent(Label).color = new Color().fromHEX('d95763')

        if (damageValue <= 0) {
            damageNode.getComponent(Label).string = 'Miss'
        }
        else {
            damageNode.getComponent(Label).string = `-${damageValue}`
        }

        damageNode.setPosition(this.node.position)
        find('Canvas').addChild(damageNode)
        tween(damageNode).by(0.8, {'position': new Vec3(0, 60, 0)}, {'easing': 'sineOut'})
                    .call(()=>{damageNode.removeFromParent()})
                    .start()
    }

    /** 
     * @zh
     * Boss生气(血量掉到一半以下时会执行这个函数)。
    */
    getAngry() {
        this.isAngry = true
        this.speed = 3
        this.damage = 300
        this.pixelNumDropped = 30
        this.bossAnimationComp.play('bossAngryMove')
        this.node.getComponent(Sprite).color = new Color().fromHEX('ff5050')
    }

    /** 
     * @zh
     * 显示像素块，打boss过程中就会掉落像素块。
    */
    spawnPixelItem() {
        let randomIndex = Math.floor(Math.random() * colorHexArray.length)
        let colorHex = colorHexArray[randomIndex]
        let pixelNum = Math.round(Math.random() * this.pixelNumDropped + this.pixelNumDropped / 10)
        let pixelItem = instantiate(this.pixelItemPrefab)
        pixelItem.getComponent(PixelItem).pixelNum = pixelNum
        pixelItem.getComponent(Sprite).color = new Color().fromHEX(colorHex)
        pixelItem.setPosition(this.node.position)
        pixelItem.getComponent(PixelItem).enabled = true
        pixelItem.setScale(1+0.1*pixelNum, 1+0.1*pixelNum, 1)
        find('Canvas').addChild(pixelItem)
    }


    /** 
     * @zh
     * 显示装备制作图。
    */
    spawnEquipmentItem(dir: Vec3) {
        // 一定概率不掉落任何物品
        let randomNum = Math.random()
        if (randomNum <= 0.7) {
            return
        }

        // 大概率出现治疗药水
        let equipmentItem = instantiate(this.equipmentItemPrefab)
        if (randomNum > 0.9) {
            resources.load(this.equipmentJsonData['cure']['picPath'], SpriteFrame, (err: any, spriteFrame: SpriteFrame) => {
                equipmentItem.children[0].getComponent(Sprite).spriteFrame = spriteFrame
                equipmentItem.name = 'equipment-cure'
            })
        }

        // 小概率出现其他物品
        else {
            let equipmentKeys = Object.keys(this.equipmentJsonData)
            let randomIndex = Math.floor(Math.random()*equipmentKeys.length)
            let randomEquipmentName = equipmentKeys[randomIndex]
            resources.load(this.equipmentJsonData[randomEquipmentName]['picPath'], SpriteFrame, (err: any, spriteFrame: SpriteFrame) => {
                equipmentItem.children[0].getComponent(Sprite).spriteFrame = spriteFrame
                equipmentItem.name = 'equipment-'+randomEquipmentName
            })
        }
        
        // 制作图不和pixelItem节点重合
        equipmentItem.setPosition(new Vec3(this.node.position).add3f(dir.x*equipmentItem.getComponent(UITransform).width, 0, 0))
        find('Canvas').addChild(equipmentItem)
    }

    /** 
     * @zh
     * 随机攻击。
    */
    attackRandomly() {
        this.schedule(()=>{
            let randomTime = Math.random() * 3 + 1
            this.scheduleOnce(()=>{this.attack()}, randomTime)
        }, 3)
    }

    /** 
     * @zh
     * 攻击逻辑。
    */
    attack() {
        // 如果正在攻击或者玩家已经倒下，则直接返回
        if (this.isAttacking || find('Canvas/Player').getComponent(Player).isDead) {
            return
        }

        // 判断距离
        let targetNodePos = find('Canvas/Player').position
        let distance = new Vec3(this.node.position).subtract(targetNodePos).length()

        // 如果距离太大或者玩家处于无敌状态的话，则不攻击
        if (distance > this.node.getComponent(UITransform).width * 2 || find('Canvas/Player').getComponent(Player).isInvincible) {
            return
        }

        // 执行攻击逻辑
        let dir = new Vec3(this.node.position).subtract(targetNodePos).normalize()
        this.isAttacking = true
        this.bossAnimationComp.stop()
        if (this.isAngry) {
            this.bossAnimationComp.play('bossAngryAttack')
            tween(this.node).by(0.5, {'position': new Vec3(-dir.x*200, -dir.y*200, 0)}, {'easing': 'sineInOut'})
                            .call(()=>{this.isAttacking=false; this.bossAnimationComp.play('bossAngryMove')})
                            .start()
        }
        else {
            this.bossAnimationComp.play('bossDefaultAttack')
            tween(this.node).by(0.8, {'position': new Vec3(-dir.x*150, -dir.y*150, 0)}, {'easing': 'sineInOut'})
                            .call(()=>{this.isAttacking=false; this.bossAnimationComp.play('bossDefaultMove')})
                            .start()
        }
    }

    /** 
     * @zh
     * 播放Boss攻击音效。
    */
    playBossAttackAudio() {
        this.audioSource.playOneShot(this.bossAttackAudio)
    }

    update(deltaTime: number) {
        if (!this.isDead && !this.isAttacking) {
            if (this.gameManager.isGamePaused) {
                this.move(this.speed/10)
            }
            else {
                this.move(this.speed)
            }
        }
    }
}


